﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Pavilion;
using Microsoft.Xna.Framework.Graphics;

namespace Pavilion.GameTools
{
    public class PhysicalTypeTool : GameToolBase
    {
        public static readonly Dictionary<string, PhysicalData> PhysicalDataCollection = new Dictionary<string, PhysicalData>();
        private static Dictionary<string, Action<PhysicalTypeTool, object>> dataExecutionDictionary;

        public PolygonPhysicalData PolygonData { get { return polygonData; } }
        public GroundState GroundState { get { return groundState; } private set { groundState = value; OnPropertyChanged("GroundState"); } }

        public Momentum Momentum { set { PolygonData.Momentum = value; } }
        public PhysicalState PhysicalState { set { PolygonData.PhysicalState = value; } }
        public bool IsFlippedHorizontally { set { PolygonData.IsFlippedHorizontally = value; } }
        public float[] Origin { set { if (value.Length >= 2) polygonData.Origin = new Vector2(value[0], value[1]); } }
        public double DrawOrder { get { return drawItem.DrawOrder; } set { drawItem.DrawOrder = value; } }
        public float Size { set { PolygonData.Size = value; } }
        public float SpeedX { set { PolygonData.SpeedX = value; } }
        public float SpeedY { set { PolygonData.SpeedY = value; } }
        public float[] Position { set { if (value.Length >= 2) PolygonData.SetPosition(value[0], value[1]); } }
        public float[] Speed { set { if (value.Length >= 2)  PolygonData.SpeedX = value[0]; PolygonData.SpeedY = value[1]; } }
        public float[] PhysicalBounds { set { SetPhysicalBounds(value); } }

        private PolygonCollisionData physicalCollisionData;
        private MultiLineCollisionData multiLineCollisionData;
        private Color boxColor = Color.Black;
        private GroundState groundState;
        private DrawItem drawItem;
        private PolygonPhysicalData polygonData;

        public PhysicalTypeTool(GameItem parentGameItem) : base(parentGameItem)
        {
            polygonData = new PolygonPhysicalData(this);
            CollisionManager.Instance.AddPolygonData(PolygonData);

            LoadDataExecution();
        }

        public override void Dispose()
        {
            ParentGameItem.UpdateLogic -= ParentGameItem_UpdateLogic;

            PhysicalDataCollection.Remove(ParentGameItem.Name);
            CollisionManager.Instance.RemovePolygonData(PolygonData);
            drawItem.Dispose();

            base.Dispose();
        }

        private void LoadDataExecution()
        {
            if (dataExecutionDictionary != null)
                return;

            dataExecutionDictionary = new Dictionary<string, Action<PhysicalTypeTool, object>>();

            dataExecutionDictionary.Add("Momentum", (physicalTypeTool, value) => physicalTypeTool.Momentum = (Momentum)value);
            dataExecutionDictionary.Add("PhysicalState", (physicalTypeTool, value) => physicalTypeTool.PhysicalState = (PhysicalState)value);
            dataExecutionDictionary.Add("IsFlippedHorizontally", (physicalTypeTool, value) => physicalTypeTool.IsFlippedHorizontally = (bool)value);
            dataExecutionDictionary.Add("Size", (physicalTypeTool, value) => physicalTypeTool.Size = (float)value);
            dataExecutionDictionary.Add("Origin", (physicalTypeTool, value) => physicalTypeTool.Origin = (float[])value);
            dataExecutionDictionary.Add("DrawOrder", (physicalTypeTool, value) => physicalTypeTool.DrawOrder = (double)value);
            dataExecutionDictionary.Add("SpeedX", (physicalTypeTool, value) => physicalTypeTool.SpeedX = (float)value);
            dataExecutionDictionary.Add("SpeedY", (physicalTypeTool, value) => physicalTypeTool.SpeedY = (float)value);
            dataExecutionDictionary.Add("Speed", (physicalTypeTool, value) => physicalTypeTool.Speed = (float[])value);
            dataExecutionDictionary.Add("Position", (physicalTypeTool, value) => physicalTypeTool.Position = (float[])value);
            dataExecutionDictionary.Add("PhysicalBounds", (physicalTypeTool, value) => physicalTypeTool.PhysicalBounds = (float[])value);
            dataExecutionDictionary.Add("GroundState", (physicalTypeTool, value) => physicalTypeTool.GroundState = (GroundState)value);
        }

        public override void UpdateProperty(string propertyName, object propertyValue)
        {
            dataExecutionDictionary[propertyName].Invoke(this, propertyValue);
        }

        public override void Load()
        {
            if (!PhysicalDataCollection.ContainsKey(ParentGameItem.Name) && ParentGameItem.IsEnabled)
                PhysicalDataCollection.Add(ParentGameItem.Name, PolygonData);

            CollisionManager.Instance.RegisterToPolygonData(PolygonData, MultiLineCollisionDataChanged);
            CollisionManager.Instance.RegisterToPolygonData(PolygonData, (PolygonCollisionData p) => UpdateBoxColor());
            physicalCollisionData = CollisionManager.Instance.GetPolygonToPolygonsData(PolygonData);
            multiLineCollisionData = CollisionManager.Instance.GetPolygonToMultiLinesData(PolygonData);

            UpdateGroundState(multiLineCollisionData.InteresectedLines);

            AnimationTool animationTool = ParentGameItem.ObtainTool<AnimationTool>();

            drawItem = new DrawItem(1, () =>
            {
                SpriteEffects spriteEffect = SpriteEffects.None;

                if (PolygonData.IsFlippedHorizontally)
                    spriteEffect = SpriteEffects.FlipHorizontally;

                animationTool.Draw(PolygonData.Position, polygonData.Size, polygonData.Origin, spriteEffect);
                DrawManager.Instance.DrawPolygonBox(PolygonData.PhysicalBounds, boxColor);
            });
            DrawManager.Instance.Add(drawItem);
            ParentGameItem.DrawVisibilityGroup.Add(drawItem);

            ParentGameItem.UpdateLogic += ParentGameItem_UpdateLogic;
        }

        private void MultiLineCollisionDataChanged(MultiLineCollisionData collisionData)
        {
            UpdateBoxColor();

            UpdateGroundState(collisionData.InteresectedLines);
        }

        private void UpdateGroundState(List<LineVector> interestectedLines)
        {
            if (interestectedLines.Count > 0)
                GroundState = GroundState.SolidGround;
            else
                GroundState = GroundState.NoGround;
        }

        private void UpdateBoxColor()
        {
            if (physicalCollisionData.IntersectedPolygons.Count > 0 && multiLineCollisionData.InteresectedLines.Count > 0)
                boxColor = Color.Orange;
            else if (physicalCollisionData.IntersectedPolygons.Count > 0)
                boxColor = Color.Red;
            else if (multiLineCollisionData.InteresectedLines.Count > 0)
                boxColor = Color.Green;
            else
                boxColor = Color.Black;
        }

        private void SetPhysicalBounds(float[] value)
        {
            PolygonData.PhysicalBounds.PerformMultipleChanges((polygonBox) =>
            {
                polygonBox.Clear();
                for (int i = 0; i < value.Length; i += 2)
                    polygonBox.Add(new Vector2(value[i], value[i + 1]));
            });
        }

        private void ParentGameItem_UpdateLogic(GameTime gameTime)
        {
            PolygonData.MoveBySpeed();
        }
    }

    public enum GroundState
    {
        SolidGround,
        SwampGround,
        NoGround,
    }
}
